⚡ 30 秒速记
- 数据赋值与页面更新不是同一时刻
- 同轮更新入队并去重,减少重复渲染
- 调度顺序考虑父子与新增任务
- 修改后
await nextTick()等待本轮刷新 - 页面已更新不代表浏览器一定已绘制
异步更新是先把数据变化记下来,再集中刷新页面,避免每次赋值都重复渲染。 同一轮里多次修改同一组件相关数据,调度器可以去重并按合适顺序处理。需要读取更新后的节点时,在修改之后等待 nextTick。它等待框架的刷新边界,不是通用定时器,也不保证图片、网络或浏览器绘制已经完成,等待对象要与业务需求一致。
版本校准: 本文出现 Object.defineProperty、Dep、Watcher 和双端 Diff 时,主要描述 Vue 2 实现;Vue 3 使用 Proxy、effect 与新的渲染器路径。Vue 2 已于 2023-12-31 结束维护,新项目应以 Vue 3 为基线,旧项目参考 Vue 2 EOL 官方说明 制定迁移与安全策略。
本文主要分析 Vue 从 Data 更新,到通知 Watcher 异步更新视图的流程,也就是下图中的橙色部分。

我们先来回顾一下图中的几个对象:
- Data 对象:Vue 中的 data 方法中返回的对象。
- Dep 对象:每一个 Data 属性都会创建一个 Dep,用来搜集所有使用到这个 Data 的 Watcher 对象。
- Watcher 对象:主要用于渲染 DOM。
接下来,我们就开始分析这个流程。
# Vue 异步更新 DOM 原理
很多同学都知道,Vue 中的数据更新是异步的,意味着我们在修改完 Data 之后,并不能立刻获取修改后的 DOM 元素。
<template>
<div>
<span id="text">{{ message }}</span>
<button @click="changeData">
changeData
</button>
</div>
</template>
<script>
export default {
data() {
return {
message: "hello",
};
},
methods: {
changeData() {
this.message = "hello world";
const textContent = document.getElementById("text").textContent;
// 直接获取,不是最新的
console.log(textContent === "hello world"); // false
// $nextTick 回调中,是最新的
this.$nextTick(() => {
const textContent = document.getElementById("text").textContent;
console.warn(textContent === "hello world"); // true
});
},
},
};
</script>